using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using OpenComposite.Properties;

namespace OpenComposite.Base
{
	/// <summary>
	/// Creates a Cursor from any bitmap.
	/// You can use the alpha-channel for transparency effects.
	/// </summary>
	public class BitmapCursor : IDisposable
	{
		#region Win-API imports

		/// <summary>
		/// API-Structure ICONINFO
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct ICONINFO
		{
			public bool fIcon;
			public uint xHotspot;
			public uint yHotspot;
			public IntPtr hbmMask;
			public IntPtr hbmColor;
		}

		/// <summary>
		/// Gets the icon info.
		/// </summary>
		/// <param name="hIcon">The icon pointer.</param>
		/// <param name="pIconInfo">A Refernce to the icon info.</param>
		/// <returns>Returns <c>true</c> if icon was successfully added to the <see cref="Z:ICONINFO"/> structure.</returns>
		[DllImport("user32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool GetIconInfo(IntPtr hIcon, ref ICONINFO pIconInfo);
		/// <summary>
		/// API function to create an icon or cursor from a bitmap.
		/// </summary>
		/// <param name="iconinfo">A <see cref="T:ICONINFO"/> structure.</param>
		/// <returns>An handle to the created icon or cursor.</returns>
		[System.Runtime.InteropServices.DllImport("USER32.DLL")]
		public static extern IntPtr CreateIconIndirect(ref ICONINFO iconinfo);
		/// <summary>
		/// API function to destroy an icon.
		/// </summary>
		/// <param name="hIcon">The <see cref="T:System.IntPtr"/> of an icon handle.</param>
		/// <returns>Returns <c>true</c> if icon was successfully destroyed.</returns>
		[System.Runtime.InteropServices.DllImport("USER32.DLL")]
		public static extern bool DestroyIcon(IntPtr hIcon);

		#endregion

		#region private

		private ICONINFO iconInfo;
		private Cursor cursor = null;
		private IntPtr handle = IntPtr.Zero;
		private DragDropEffects _effect = DragDropEffects.Move;

		private void Create()
		{
			handle = CreateIconIndirect(ref iconInfo);
			if ( handle != IntPtr.Zero ) {
				cursor = new Cursor(handle);
			}
		}


		#endregion

		#region constructors and destructor

		/// <summary>
		/// Initializes a new instance of the <see cref="BitmapCursor"/> class.
		/// </summary>
		/// <param name="bmp">The bitmap.</param>
		/// <param name="HotSpotX">The hot spot X.</param>
		/// <param name="HotSpotY">The hot spot Y.</param>
		public BitmapCursor(System.Drawing.Bitmap bmp, uint HotSpotX, uint HotSpotY)
		{
			iconInfo = new ICONINFO();
			GetIconInfo(bmp.GetHicon(), ref iconInfo);
			iconInfo.fIcon = false;
			iconInfo.xHotspot = HotSpotX;
			iconInfo.yHotspot = HotSpotY;
			Create();
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="BitmapCursor"/> class 
		/// from a bitmap and combines it with another cursor.
		/// </summary>
		/// <param name="bmp">The bitmap.</param>
		/// <param name="cursor">The cursor.</param>
		public BitmapCursor(System.Drawing.Bitmap bmp, Cursor cursor)
		{
			iconInfo = new ICONINFO();
			using ( System.Drawing.Bitmap bmpdup = bmp.Clone() as System.Drawing.Bitmap ) {
				using ( Graphics g = Graphics.FromImage(bmpdup) ) {
					cursor.Draw(g, new Rectangle(new Point(0, 0), cursor.Size));
				}
				GetIconInfo(bmpdup.GetHicon(), ref iconInfo);
				iconInfo.xHotspot = (uint)cursor.HotSpot.X;
				iconInfo.yHotspot = (uint)cursor.HotSpot.Y;
				iconInfo.fIcon = false;
				Create();
			}
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="BitmapCursor"/> class
		/// by combining a cursor and a bitmap.
		/// </summary>
		/// <param name="cursor">The cursor.</param>
		/// <param name="bmp">The bitmap.</param>
		public BitmapCursor(Cursor cursor, Bitmap bmp)
			: this(cursor, bmp, DragDropEffects.None)
		{
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="BitmapCursor"/> class
		/// by combining a cursor and a bitmap.
		/// </summary>
		/// <param name="cursor">The cursor.</param>
		/// <param name="bmp">The bitmap.</param>
		public BitmapCursor(Cursor cursor, Bitmap bmp, DragDropEffects effect)
		{
			_effect = effect;

			iconInfo = new ICONINFO();

			// get hotspot
			int curhotx = cursor.HotSpot.X;
			int curhoty = cursor.HotSpot.Y;
			// do not move bitmap and effect icon if hotspot is not near top-left corner
			if ( curhotx > 15 ) curhotx = 0;
			if ( curhoty > 15 ) curhoty = 0;
			// get cursor width and height
			int curw = cursor.Size.Width;
			int curh = cursor.Size.Height;
			// if cursor is to small then set new reference size
			if ( curw < 32 ) curw = 32;
			if ( curh < 32 ) curh = 32;
			// calculate bitmap postion
			int bmpposx = (int)( curw / 2 ) + curhotx;
			int bmpposy = (int)( curh / 2 ) + curhoty;
			// set total bitmapcursor size
			int width = bmpposx + bmp.Width + 1;
			int height = bmpposy + bmp.Height + 1;
			// if bitmap cursor size smaller than cursor set bitmap cursor size to cursor size
			if ( width < curw ) width = curw;
			if ( height < curh ) height = curh;

			using ( System.Drawing.Bitmap bmpcur = new Bitmap(width, height, PixelFormat.Format32bppPArgb | PixelFormat.Gdi) ) {
				using ( Graphics g = Graphics.FromImage(bmpcur) ) {
					// set rending options
					//g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
					g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
					// clear graphic
					g.Clear(Color.Transparent);//Color.FromArgb(0, 255, 255, 255));
					// draw bitmap
					g.DrawImage(bmp, bmpposx, bmpposy);
					// draw cursor
					using ( Icon icur = Icon.FromHandle(cursor.Handle) ) {
						g.DrawIcon(icur, new Rectangle(new Point(0, 0), cursor.Size));
						BitmapCursor.DestroyIcon(icur.Handle);
					}
					// draw drag'n'drop effect icon
					if ( _effect == DragDropEffects.Copy ) {
						g.DrawImage(Resources.copy_11_png, curhotx, curh - 11 + curhoty);
					} else if ( _effect == DragDropEffects.Link ) {
						g.DrawImage(Resources.link_11_png, curhotx, curh - 11 + curhoty);
					} else if ( _effect == DragDropEffects.Move ) {
						g.DrawImage(Resources.move_11_png, curhotx, curh - 11 + curhoty);
					}
				}
				GetIconInfo(bmpcur.GetHicon(), ref iconInfo);
				iconInfo.xHotspot = (uint)cursor.HotSpot.X;
				iconInfo.yHotspot = (uint)cursor.HotSpot.Y;
				iconInfo.fIcon = false;
				Create();
			}
		}


		/// <summary>
		/// Releases unmanaged resources and performs other cleanup operations before the
		/// <see cref="BitmapCursor"/> is reclaimed by garbage collection.
		/// </summary>
		~BitmapCursor()
		{
			Dispose(false);
		}

		#endregion

		#region virtual methods

		/// <summary>
		/// Releases unmanaged and - optionally - managed resources
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
		protected virtual void Dispose(bool disposing)
		{
			try {
				if ( handle != IntPtr.Zero )
					DestroyIcon(handle);
			} catch {
			}
		}


		#endregion

		#region public properties

		/// <summary>
		/// Gets the cursor object you can use.
		/// </summary>
		/// <value>The cursor.</value>
		public Cursor Cursor
		{
			get
			{
				return cursor;
			}
		}
		/// <summary>
		/// Gets the drag drop effect.
		/// </summary>
		/// <value>The effect.</value>
		public DragDropEffects Effect
		{
			get { return _effect; }
		}

		#endregion

		#region IDisposable Member

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			GC.SuppressFinalize(this);
			Dispose(true);
		}

		#endregion
	}
}

